﻿using Rhino;
using Rhino.Commands;
using Rhino.Display;
using Rhino.DocObjects;
using Rhino.Geometry;
using Rhino.Input.Custom;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Threading;
namespace RhpCompile
{
    public abstract class RhpBaseScripts : Command
    {
        readonly Previewer previewer;
        public RhpBaseScripts()
        {
            previewer = Previewer.Instance;
            try
            {
                OnLoad();
            }
            catch (Exception ex)
            {
                RhinoApp.WriteLine("警告：载入时OnLoad()代码运行出错：\n" + ex.ToString());
            }
        }
        public void Dispose()
        {
            LoopOff();
            previewer.ClosePreview();
        }
        public virtual void OnLoad()
        {
        }
        protected abstract void Run();
        protected virtual string RunCommandInfo => "";
        protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            try
            {
                LoopOff();
                previewer.Clear();
                previewer.EnableDraw();
                Run();
                if (previewer.IsEmpty && (!OnLoop))
                {
                    previewer.ClosePreview();
                }
                RhinoDoc.ActiveDoc.Views.Redraw();
                if (!string.IsNullOrEmpty(RunCommandInfo))
                {
                    Print(RunCommandInfo);
                }
                return Result.Success;
            }
            catch (Exception ex)
            {
                RhinoApp.WriteLine($"{EnglishName} Command Error: ");
                RhinoApp.WriteLine(ex.Message);
                RhinoApp.WriteLine(ex.StackTrace);
            }
            return Result.Failure;
        }
        #region Show
        public void Show(object geometry, Color color)
        {
            previewer.Preview(geometry, color);
        }
        public void Show(object geometry)
        {
            previewer.Preview(geometry, Previewer.DefaultColor);
        }
        /// <summary>
        /// 重绘所有Rhino视图
        /// </summary>
        public void RedrawAll()
        {
            RhinoDoc.ActiveDoc.Views.Redraw();
        }
        public void Redraw()
        {
            RhinoDoc.ActiveDoc.Views.ActiveView.Redraw();
        }
        public void ClearShow()
        {
            previewer.Clear();
        }
        public void Show<T>(IEnumerable<T> geometries)
        {
            if (geometries is Polyline polyline)
            {
                previewer.Preview(polyline, Previewer.DefaultColor);
                return;
            }
            foreach (T geo in geometries)
                previewer.Preview(geo, Previewer.DefaultColor);
        }

        public void Show<T>(IEnumerable<T> geometries, Color color)
        {
            if (geometries is Polyline polyline)
            {
                previewer.Preview(polyline, color);
                return;
            }
            foreach (T geo in geometries)
                previewer.Preview(geo, color);
        }
        public void Show(Vector3d vector3d, Point3d from, Color color)
        {
            previewer.Preview(vector3d, from, color);
        }
        public void Show(Vector3d vector3d, Point3d from)
        {
            previewer.Preview(vector3d, from, Previewer.DefaultColor);
        }

        public void Show(string text, Point3d location, int height = 12)
        {
            Show(text, location, height, Previewer.DefaultColor);
        }
        public void Show(string text, Point3d location, int height, Color color)
        {
            TextDot textDot = new TextDot(text, location)
            {
                FontHeight = height,
            };
            Show(textDot, color);
        }
        public void Show(string text, Plane plane, double height)
        {
            Color color = Color.FromArgb(255, Previewer.DefaultColor);

            Show(text, plane, height, color);
        }
        public void Show(string text, Plane plane, double height, Color color)
        {
            Text3d text3D = new Text3d(text, plane, height)
            {
                HorizontalAlignment = TextHorizontalAlignment.Center,
                VerticalAlignment = TextVerticalAlignment.Middle,
            };
            Show(text3D, color);
        }
        #endregion
        #region Select
        class GetCircleObject : GetObject
        {
            public override bool CustomGeometryFilter(RhinoObject rhObject, GeometryBase geometry, ComponentIndex componentIndex)
            {
                return geometry.ObjectType == ObjectType.Curve && geometry is Curve crv && crv.TryGetCircle(out _);
            }
        }
        class GetLineObject : GetObject
        {
            public override bool CustomGeometryFilter(RhinoObject rhObject, GeometryBase geometry, ComponentIndex componentIndex)
            {
                return geometry.ObjectType == ObjectType.Curve && geometry is Curve crv && crv.IsLinear();
            }
        }
        class GetPolylineObject : GetObject
        {
            public override bool CustomGeometryFilter(RhinoObject rhObject, GeometryBase geometry, ComponentIndex componentIndex)
            {
                return geometry.ObjectType == ObjectType.Curve && geometry is Curve crv && crv.TryGetPolyline(out _);
            }
        }
        class GetTextEntityObject : GetObject
        {
            public override bool CustomGeometryFilter(RhinoObject rhObject, GeometryBase geometry, ComponentIndex componentIndex)
            {
                return geometry.ObjectType == ObjectType.Annotation && geometry is TextEntity text && text.IsValid;
            }
        }
        void ClearSelect()
        {
            RhinoDoc.ActiveDoc.Objects.UnselectAll();
        }
        public bool Select(out RhinoObject[] rhinoObjects) => Select(out rhinoObjects, ObjectType.AnyObject, "");
        public bool Select(out RhinoObject[] rhinoObjects, ObjectType objectType) => Select(out rhinoObjects, objectType, "");
        /// <summary>
        /// 选取多个Rhino物体，可以指定选择的ObjectType。
        /// 成功返回True。
        /// </summary>
        public bool Select(out RhinoObject[] rhinoObjects, ObjectType objectType, string prompts)
        {
            GetObject get = new GetObject();
            if (string.IsNullOrEmpty(prompts)) prompts = $"请选择多个物体：{objectType}";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.GeometryFilter = objectType;
            get.SubObjectSelect = false;
            bool re = get.GetMultiple(1, 0) == Rhino.Input.GetResult.Object && get.ObjectCount > 0;
            if (re)
                rhinoObjects = get.Objects().Select(x => x.Object()).ToArray();
            else
                rhinoObjects = null;
            ClearSelect();
            return re;
        }
        public bool Select(out RhinoObject rhinoObject) => Select(out rhinoObject, ObjectType.AnyObject, "");
        public bool Select(out RhinoObject rhinoObject, ObjectType objectType) => Select(out rhinoObject, objectType, "");
        /// <summary>
        /// 选取一个Rhino物体，可以指定选择的ObjectType。
        /// 成功返回True。
        /// </summary>
        public bool Select(out RhinoObject rhinoObject, ObjectType objectType, string prompts)
        {
            GetObject get = new GetObject();
            if (string.IsNullOrEmpty(prompts)) prompts = $"请选择一个物体：{objectType}";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.GeometryFilter = objectType;
            get.SubObjectSelect = false;
            bool re = get.Get() == Rhino.Input.GetResult.Object && get.ObjectCount > 0;
            if (re)
                rhinoObject = get.Object(0).Object();
            else
                rhinoObject = null;
            ClearSelect();
            return re;
        }
        public bool Select(out Polyline polyline) => Select(out polyline, "");
        /// <summary>
        /// 选取一个多段线，如果成功返回True
        /// </summary>
        public bool Select(out Polyline polyline, string prompts)
        {
            GetPolylineObject get = new GetPolylineObject();
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择一个多段线";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.SubObjectSelect = false;
            bool re = get.Get() == Rhino.Input.GetResult.Object && get.ObjectCount > 0;

            if (re)
            {
                Curve curve = get.Object(0).Object().Geometry as Curve;
                if (curve.TryGetPolyline(out polyline)) return true;
            }
            polyline = null;
            ClearSelect();
            return false;
        }
        public bool Select(out Polyline[] polylines) => Select(out polylines, "");
        /// <summary>
        /// 选取多个多段线，如果成功返回True
        /// </summary>
        public bool Select(out Polyline[] polylines, string prompts)
        {
            GetPolylineObject get = new GetPolylineObject();
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择多个多段线";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.SubObjectSelect = false;
            bool re = get.GetMultiple(1, 0) == Rhino.Input.GetResult.Object && get.ObjectCount > 0;
            List<Polyline> list = new List<Polyline>();
            if (re)
            {
                foreach (var obj in get.Objects())
                {
                    if (obj.Object().Geometry is Curve crv && crv.TryGetPolyline(out Polyline pl))
                    {
                        list.Add(pl);
                    }
                }
            }
            polylines = list.ToArray();
            ClearSelect();
            return list.Count > 0;
        }

        public bool Select(out Circle[] circles) => Select(out circles, "");
        public bool Select(out Circle[] circles, string prompts)
        {
            GetCircleObject get = new GetCircleObject();
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择多个圆";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.SubObjectSelect = false;
            bool re = get.GetMultiple(1, 0) == Rhino.Input.GetResult.Object && get.ObjectCount > 0;
            List<Circle> list = new List<Circle>();
            if (re)
            {
                foreach (var obj in get.Objects())
                {
                    if (obj.Object().Geometry is Curve crv && crv.TryGetCircle(out Circle cir))
                    {
                        list.Add(cir);
                    }
                }
            }
            circles = list.ToArray();
            ClearSelect();
            return list.Count > 0;
        }
        public bool Select(out Circle circle) => Select(out circle, "");
        public bool Select(out Circle circle, string prompts)
        {
            GetCircleObject get = new GetCircleObject();
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择一个圆";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.SubObjectSelect = false;
            bool re = get.Get() == Rhino.Input.GetResult.Object && get.ObjectCount > 0;
            if (re)
            {
                Curve curve = get.Object(0).Object().Geometry as Curve;
                if (curve.TryGetCircle(out circle)) return true;
            }
            circle = Circle.Unset;
            ClearSelect();
            return false;
        }
        public bool Select(out Line[] lines) => Select(out lines, "");
        public bool Select(out Line[] lines, string prompts)
        {
            GetLineObject get = new GetLineObject();
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择多个线段";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.SubObjectSelect = false;
            bool re = get.GetMultiple(1, 0) == Rhino.Input.GetResult.Object && get.ObjectCount > 0;
            List<Line> list = new List<Line>();
            if (re)
            {
                foreach (var obj in get.Objects())
                {
                    if (obj.Object().Geometry is Curve crv)
                    {
                        list.Add(new Line(crv.PointAtStart, crv.PointAtEnd));
                    }
                }
            }
            lines = list.ToArray();
            ClearSelect();
            return list.Count > 0;
        }
        public bool Select(out Line line) => Select(out line, "");
        public bool Select(out Line line, string prompts)
        {
            GetLineObject get = new GetLineObject();
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择一个线段";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.SubObjectSelect = false;
            bool re = get.Get() == Rhino.Input.GetResult.Object && get.ObjectCount > 0;
            if (re)
            {
                Curve crv = get.Object(0).Object().Geometry as Curve;
                line = new Line(crv.PointAtStart, crv.PointAtEnd);

            }
            else line = Line.Unset;
            ClearSelect();
            return re;
        }
        public bool Select(out Curve[] curves) => Select(out curves, "");
        /// <summary>
        /// 选取多个曲线，如果成功返回True
        /// </summary>
        public bool Select(out Curve[] curve, string prompts)
        {
            curve = null;
            bool re = Select(out RhinoObject[] objs, ObjectType.Curve, prompts);
            if (re)
            {
                curve = objs.Select(x => x.Geometry as Curve).ToArray();
            }
            return re;
        }
        public bool Select(out Curve curve) => Select(out curve, "");
        /// <summary>
        /// 选取一个曲线，如果成功返回True
        /// </summary>
        public bool Select(out Curve curve, string prompts)
        {
            curve = null;
            bool re = Select(out RhinoObject obj, ObjectType.Curve, prompts);
            if (re)
            {
                curve = obj.Geometry as Curve;
            }
            return re;
        }
        public bool Select(out Mesh[] meshes) => Select(out meshes, "");
        /// <summary>
        /// 选取多个网格，如果成功返回True
        /// </summary>
        public bool Select(out Mesh[] meshes, string prompts)
        {
            meshes = null;
            bool re = Select(out RhinoObject[] objs, ObjectType.Mesh, prompts);
            if (re)
            {
                meshes = objs.Select(x => x.Geometry as Mesh).ToArray();
            }
            return re;
        }
        public bool Select(out Mesh mesh) => Select(out mesh, "");
        /// <summary>
        /// 选取一个网格，如果成功返回True
        /// </summary>
        public bool Select(out Mesh mesh, string prompts)
        {
            mesh = null;
            bool re = Select(out RhinoObject obj, ObjectType.Mesh, prompts);
            if (re)
            {
                mesh = obj.Geometry as Mesh;
            }
            return re;
        }
        public bool Select(out Brep[] breps) => Select(out breps, "");
        /// <summary>
        /// 选取多个多重曲面，如果成功返回True
        /// </summary>
        public bool Select(out Brep[] breps, string prompts)
        {
            breps = null;
            bool re = Select(out RhinoObject[] objs, ObjectType.Brep, prompts);
            if (re)
            {
                breps = objs.Select(x => ToBrep(x.Geometry)).ToArray();
            }
            return re && breps.All(x => x is Brep);
        }
        public bool Select(out Brep brep) => Select(out brep, "");
        /// <summary>
        /// 选取一个多重曲面，如果成功返回True
        /// </summary>
        public bool Select(out Brep brep, string prompts)
        {
            brep = null;
            bool re = Select(out RhinoObject obj, ObjectType.Brep, prompts);
            if (re)
            {
                brep = ToBrep(obj.Geometry);
            }
            return re && brep != null;
        }
        Brep ToBrep(GeometryBase geo)
        {
            Brep bp = geo as Brep;
            if (bp == null)
            {
                if (geo is Extrusion extrusion)
                {
                    bp = extrusion.ToBrep();
                }
            }
            return bp;
        }
        public bool Select(out Extrusion[] extrusions) => Select(out extrusions, "");
        /// <summary>
        /// 选取多个挤出物件，如果成功返回True
        /// </summary>
        public bool Select(out Extrusion[] extrusions, string prompts)
        {
            extrusions = null;
            bool re = Select(out RhinoObject[] objs, ObjectType.Extrusion, prompts);
            if (re)
            {
                extrusions = objs.Select(x => x.Geometry as Extrusion).ToArray();
            }
            return re;
        }
        public bool Select(out Extrusion extrusion) => Select(out extrusion, "");
        /// <summary>
        /// 选取一个挤出物件，如果成功返回True
        /// </summary>
        public bool Select(out Extrusion extrusion, string prompts)
        {
            extrusion = null;
            bool re = Select(out RhinoObject obj, ObjectType.Extrusion, prompts);
            if (re)
            {
                extrusion = obj.Geometry as Extrusion;
            }
            return re;
        }
        public bool Select(out Surface[] surfaces) => Select(out surfaces, "");
        /// <summary>
        /// 选取多个曲面，如果成功返回True
        /// </summary>
        public bool Select(out Surface[] surfaces, string prompts)
        {
            surfaces = null;
            bool re = Select(out RhinoObject[] objs, ObjectType.Surface, prompts);
            if (re)
            {
                surfaces = objs.Select(x => x.Geometry as Surface).ToArray();
            }
            return re;
        }
        public bool Select(out Surface surface) => Select(out surface, "");
        /// <summary>
        /// 选取一个曲面，如果成功返回True
        /// </summary>
        public bool Select(out Surface surface, string prompts)
        {
            surface = null;
            bool re = Select(out RhinoObject obj, ObjectType.Surface, prompts);
            if (re)
            {
                surface = obj.Geometry as Surface;
            }
            return re;
        }
        public bool Select(out InstanceObject[] instanceObjects) => Select(out instanceObjects, "");
        /// <summary>
        /// 选取多个图块物体，如果成功返回True
        /// </summary>
        public bool Select(out InstanceObject[] instanceObjects, string prompts)
        {
            instanceObjects = null;
            bool re = Select(out RhinoObject[] objs, ObjectType.InstanceReference, prompts);
            if (re)
            {
                instanceObjects = objs.Cast<InstanceObject>().ToArray();
            }
            return re;
        }
        public bool Select(out InstanceObject instanceObject) => Select(out instanceObject, "");
        /// <summary>
        /// 选取一个图块物体，如果成功返回True
        /// </summary>
        public bool Select(out InstanceObject instanceObject, string prompts)
        {
            instanceObject = null;
            bool re = Select(out RhinoObject obj, ObjectType.InstanceReference, prompts);
            if (re)
            {
                instanceObject = obj as InstanceObject;
            }
            return re;
        }
        public bool Select(out TextEntity text) => Select(out text, "");
        public bool Select(out TextEntity text, string prompts)
        {
            GetTextEntityObject get = new GetTextEntityObject();
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择一个文字";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.SubObjectSelect = false;
            bool re = get.Get() == Rhino.Input.GetResult.Object && get.ObjectCount > 0;
            if (re)
            {
                text = get.Object(0).Object().Geometry as TextEntity;
            }
            else text = null;
            ClearSelect();
            return re;
        }
        public bool Select(out TextEntity[] texts) => Select(out texts, "");
        public bool Select(out TextEntity[] texts, string prompts)
        {
            GetTextEntityObject get = new GetTextEntityObject();
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择多个文字";
            get.SetCommandPrompt(prompts);
            get.EnablePreSelect(true, true);
            get.SubObjectSelect = false;
            bool re = get.GetMultiple(1, 0) == Rhino.Input.GetResult.Object && get.ObjectCount > 0;
            List<TextEntity> list = new List<TextEntity>();
            if (re)
            {
                foreach (var obj in get.Objects())
                {
                    if (obj.Object().Geometry is TextEntity text)
                    {
                        list.Add(text);
                    }
                }
            }
            texts = list.ToArray();
            ClearSelect();
            return list.Count > 0;
        }
        public bool Select(out Point3d pt) => Select(out pt, "");
        public bool Select(out Point3d pt, string prompts)
        {
            pt = Point3d.Unset;
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择点";
            bool re = Select(out RhinoObject obj, ObjectType.Point, prompts);
            if (re)
            {
                Rhino.Geometry.Point ptGeo = obj.Geometry as Rhino.Geometry.Point;
                if (ptGeo != null)
                    pt = ptGeo.Location;
            }
            return pt.IsValid;
        }
        public bool Select(out Point3d[] pts) => Select(out pts, "");
        public bool Select(out Point3d[] pts, string prompts)
        {
            pts = null;
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择多个点";
            bool re = Select(out RhinoObject[] objs, ObjectType.Point, prompts);
            if (re)
            {
                pts = objs.Select(x => x.Geometry).Cast<Rhino.Geometry.Point>().Select(x => x.Location).ToArray();
            }
            return re;
        }
        public bool Select(out GeometryBase geo) => Select(out geo, "");
        public bool Select(out GeometryBase geo, string prompts)
        {
            geo = null;
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择GeometryBase";
            bool re = Select(out RhinoObject rhinoObject, ObjectType.AnyObject, prompts);
            if (re)
            {
                geo = rhinoObject.Geometry;
            }
            return geo != null;
        }
        public bool Select(out GeometryBase[] geos) => Select(out geos, "");
        public bool Select(out GeometryBase[] geos, string prompts)
        {
            geos = null;
            if (string.IsNullOrEmpty(prompts)) prompts = "请选择多个GeometryBase";
            bool re = Select(out RhinoObject[] rhinoObjects, ObjectType.AnyObject, prompts);
            if (re)
            {
                geos = rhinoObjects.Select(x => x.Geometry).ToArray();
            }
            return re;
        }
        #endregion
        #region Bake
        public Guid Bake(Point3d geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid Bake(Point3f geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid Bake(PointCloud geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Point3d> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Point3d> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Point3f> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Point3f> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<PointCloud> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<PointCloud> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(Line geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Line> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Line> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(Box geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Box> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Box> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }

        public Guid Bake(BoundingBox geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<BoundingBox> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<BoundingBox> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }

        public Guid Bake(Circle geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Circle> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Circle> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }

        public Guid Bake(Arc geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Arc> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Arc> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(Ellipse geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Ellipse> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Ellipse> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }

        public Guid Bake(Cone geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Cone> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Cone> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(Rectangle3d geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Rectangle3d> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Rectangle3d> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(Polyline geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Polyline> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Polyline> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(Cylinder geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Cylinder> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Cylinder> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(Plane geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Plane> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Plane> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(Sphere geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Sphere> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Sphere> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(GeometryBase geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<GeometryBase> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<GeometryBase> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }

        public Guid Bake(RhinoObject geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<RhinoObject> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<RhinoObject> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        public Guid Bake(string text, Point3d location, int height = 12, ObjectAttributes objectAttributes = null)
        {
            TextDot textDot = new TextDot(text, location)
            {
                FontHeight = height,
            };
            return BakeOne(textDot, objectAttributes);
        }
        public Guid Bake(string text, Plane plane, double height = 12, ObjectAttributes objectAttributes = null)
        {
            Text3d text3D = new Text3d(text, plane, height)
            {
                HorizontalAlignment = TextHorizontalAlignment.Center,
                VerticalAlignment = TextVerticalAlignment.Middle,
            };
            return BakeOne(text3D, objectAttributes);
        }
        public Guid Bake(Text3d geo, ObjectAttributes objectAttributes = null)
        {
            return BakeOne(geo, objectAttributes);
        }
        public Guid[] Bake(IEnumerable<Text3d> geos)
        {
            return BakeMulti(geos);
        }
        public Guid[] Bake(IEnumerable<Text3d> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            return BakeMulti(geos, objectAttributes);
        }
        Guid[] BakeMulti<T>(IEnumerable<T> geos, IEnumerable<ObjectAttributes> objectAttributes)
        {
            List<Guid> result = new List<Guid>();
            T[] arrayGeos = geos.ToArray();
            ObjectAttributes[] arrayAttr = objectAttributes.ToArray();
            if (arrayAttr.Length == arrayGeos.Length)
            {
                for (int i = 0; i < arrayAttr.Length; i++)
                {
                    Guid id = BakeOne(arrayGeos[i], arrayAttr[i]);
                    if (id != Guid.Empty) result.Add(id);
                }
            }
            return result.ToArray();
        }
        Guid[] BakeMulti<T>(IEnumerable<T> geos)
        {
            List<Guid> result = new List<Guid>();
            foreach (T geo in geos)
            {
                Guid id = BakeOne(geo, null);
                if (id != Guid.Empty) result.Add(id);
            }
            return result.ToArray();
        }
        Guid BakeOne<T>(T geo, ObjectAttributes objectAttributes)
        {
            switch (geo)
            {
                case Point3d pt:
                    if (pt.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddPoint(pt, objectAttributes);
                    break;
                case Point3f ptf:
                    if (ptf.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddPoint(ptf, objectAttributes);
                    break;
                case PointCloud ptCloud:
                    if (ptCloud.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddPointCloud(ptCloud, objectAttributes);
                    break;
                case Line line:
                    if (line.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddLine(line, objectAttributes);
                    break;
                case Box box:
                    if (box.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddBox(box, objectAttributes);
                    break;
                case BoundingBox bbox:
                    if (bbox.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddBox(new Box(bbox), objectAttributes);
                    break;
                case Circle circle:
                    if (circle.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddCircle(circle, objectAttributes);
                    break;
                case Arc arc:
                    if (arc.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddArc(arc, objectAttributes);
                    break;
                case Ellipse ellipse:
                    if (ellipse.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddEllipse(ellipse, objectAttributes);
                    break;
                case Cone cone:
                    if (cone.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddSurface(cone.ToNurbsSurface(), objectAttributes);
                    break;
                case Rectangle3d rectangle:
                    if (rectangle.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddRectangle(rectangle, objectAttributes);
                    break;
                case Polyline polyline:
                    if (polyline.IsValid && polyline.Count > 1)
                        return RhinoDoc.ActiveDoc.Objects.AddPolyline(polyline, objectAttributes);
                    break;
                case Cylinder cylinder:
                    if (cylinder.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddBrep(cylinder.ToBrep(true, true), objectAttributes);
                    break;
                case Plane plane:
                    return BakePlane(plane, objectAttributes);
                case Sphere sphere:
                    if (sphere.IsValid)
                        return RhinoDoc.ActiveDoc.Objects.AddSphere(sphere, objectAttributes);
                    break;
                case GeometryBase geometry:
                    return BakeGeometry(geometry, objectAttributes);
                case RhinoObject rhinoObject:
                    if (objectAttributes == null)
                        objectAttributes = rhinoObject.Attributes;
                    if (rhinoObject is InstanceObject instanceObject)
                        return RhinoDoc.ActiveDoc.Objects.AddInstanceObject(instanceObject.InstanceDefinition.Index, instanceObject.InstanceXform, objectAttributes);
                    else
                        return RhinoDoc.ActiveDoc.Objects.Add(rhinoObject.Geometry, objectAttributes);
                case Text3d text3D:
                    return RhinoDoc.ActiveDoc.Objects.AddText(text3D, objectAttributes);
            }
            return Guid.Empty;
        }
        Guid BakeGeometry(GeometryBase geometry, ObjectAttributes objectAttributes)
        {
            if (geometry.IsValid)
                return RhinoDoc.ActiveDoc.Objects.Add(geometry, objectAttributes);
            else
            {
                switch (geometry)
                {
                    case Mesh mesh:
                        return RhinoDoc.ActiveDoc.Objects.AddMesh(mesh, objectAttributes, null, false, false);
                    case Brep brep:
                        return RhinoDoc.ActiveDoc.Objects.AddBrep(brep, objectAttributes, null, false, false);
                    default:
                        return RhinoDoc.ActiveDoc.Objects.Add(geometry, objectAttributes);
                }
            }
        }
        Guid BakePlane(Plane plane, ObjectAttributes objectAttributes)
        {
            Point3d pt = RhinoDoc.ActiveDoc.Views.ActiveView.ActiveViewport.CameraTarget;
            RhinoDoc.ActiveDoc.Views.ActiveView.ActiveViewport.GetWorldToScreenScale(pt, out double pixelPerUnit);
            double s = 40 / pixelPerUnit;
            Polyline pl = new Polyline(10)
            {
                plane.PointAt(0,0,s),
                plane.Origin,
                plane.PointAt(s,0,0),
                plane.Origin,
                plane.PointAt(0,s,0),
                plane.PointAt(s,s,0),
                plane.PointAt(s,-s,0),
                plane.PointAt(-s,-s,0),
                plane.PointAt(-s,s,0),
                plane.PointAt(0,s,0)
            };
            return RhinoDoc.ActiveDoc.Objects.AddPolyline(pl, objectAttributes);
        }
        #endregion
        #region Print

        public void Print(string text)
        {
            RhinoApp.WriteLine(text);
        }
        public void Print(string content, params object[] args)
        {
            RhinoApp.WriteLine(string.Format(content, args));
        }
        public void Print(object obj)
        {
            if (obj is IEnumerable iEnumerable)
            {
                string name = iEnumerable.GetType().Name;
                name = name.Substring(0, name.Length - 2);

                string content = $"{name}" + " { ";
                int count = 0;
                foreach (object obj2 in iEnumerable)
                {
                    if (count == 0)
                    {
                        content += obj2.ToString();
                    }
                    else if (count < 10)
                    {
                        content += ", " + obj2.ToString();
                    }
                    else if (count == 10)
                    {
                        content += $", ......";
                    }
                    count++;
                }
                content += " } " + $" ({count})";
                RhinoApp.WriteLine(content);
            }
            else if (obj is string ss)
            {
                RhinoApp.WriteLine(ss);
            }
            else RhinoApp.WriteLine($"[{obj.GetType()}] {obj}");
        }
        #endregion
        #region Loop

        Thread loopThread;
        /// <summary>
        /// 开启循环
        /// </summary>
        public void LoopOn()
        {
            if (!OnLoop)
            {
                Type typeFromHandle = typeof(RhpBaseScripts);
                Type type = GetType();
                if (type.GetMethod("Loop", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).DeclaringType != typeFromHandle)
                {
                    OnLoop = true;
                    previewer.LoopOn = true;
                    loopThread = new Thread(StartLoop);
                    loopThread.Start();
                }
            }
        }
        public void LoopOff()
        {
            OnLoop = false;
            Thread.Sleep(300);
            loopThread?.Abort();
        }
        internal bool OnLoop { get; private set; } = true;
        void StartLoop()
        {
            try
            {
                while (OnLoop && previewer.Enabled)
                {
                    Loop();
                }
            }
            catch (Exception ex)
            {
                RhinoApp.WriteLine("Loop() exit with exception:\n" + ex.ToString());
            }
        }
        public virtual void Loop()
        {

        }

        /// <summary>
        /// 暂停一段时间
        /// </summary>
        public void Sleep(int milliseconds)
        {
            Thread.Sleep(milliseconds);
        }

        #endregion
    }
}
